package service.impl;

import bean.Goods;
import bean.Sale;
import dao.GoodsDao;
import dao.SaleDao;
import exception.NotEnoughException;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import service.BuyGoodsService;

/**
 * @author HLF
 * @version V1.0
 * @package service.impl
 * @date 2021/10/21 20:04
 * <p>
 * 这样没有事务时，销售记录增加了，但是库存不足时或者商品找不到时销售记录却增加了
 * 应该增加事务功能，使用事务的回滚
 * 如果增加了事务，二者共同成功或失败
 */

/**
 * 若@Transactional 注解在类上，则表示该类上所有的方法均将在执行时加入事务。
 * <p>
 * rollbackFor:表示发生指定异常时一定会滚
 * 处理逻辑是：
 * 1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
 * 如果异常在rollbackFor列表中，不管是什么类型的异常，一定回滚。
 * 2) 如果你的抛出的异常不在rollbackFor列表中，spring会判断异常是不是RuntimeException,
 * 如果是一定回滚。
 */
//@Transactional
public class BuyGoodsServiceImpl implements BuyGoodsService {
    private GoodsDao goodsDao = null;
    private SaleDao saleDao = null;

    @Transactional(
            propagation = Propagation.REQUIRED,//事务的传播行为,这里使用的是默认的
            isolation = Isolation.DEFAULT,//事务的隔离级别，mysql默认是Isolation.REPEATABLE_READ
            readOnly = false,//是否对数据库只读，这里为否，如果只有查询操作可以只读（效率会更高），这里false是默认的
            rollbackFor = {
                    NullPointerException.class, NotEnoughException.class
            }//需要回滚的异常类（当出现这些异常时需要回滚），当不具体写这些异常时，默认抛出的是运行时异常
    )
    //如果上述值都是使用的默认值，则可以直接使用注解而不加属性值，这样比较简便
    //@Transactional
    @Override
    public void buy(Integer goodsId, Integer nums) {//需要加事务的方法必须是public（公共的）
        System.out.println("buy方法开始");

        //1.记录销售信息，向sale表添加记录
        Sale sale = new Sale(null, goodsId, nums);
        saleDao.insertSale(sale);

        //2.更新库存
        Goods goods = goodsDao.selectGoods(goodsId);
        if (goods == null) {
            //商品不存在
            throw new NullPointerException("编号为" + goodsId + "的商品不存在！");
        } else if (nums > goods.getAmount()) {
            //商品库存不足
            throw new NotEnoughException("编号为" + goodsId + "的商品数量不足！");
        } else {
            //修改库存
            Goods buyGoods = new Goods();
            buyGoods.setId(goodsId);
            buyGoods.setAmount(nums);
            goodsDao.updateGoods(buyGoods);

            System.out.println("buy方法结束");
        }
    }


    //set设值注入给goodsDao赋值
    public void setGoodsDao(GoodsDao goodsDao) {
        this.goodsDao = goodsDao;
    }

    //set设值注入给saleDao赋值
    public void setSaleDao(SaleDao saleDao) {
        this.saleDao = saleDao;
    }
}
